import {
  Component,
  ElementRef,
  HostBinding,
  OnInit,
  ViewContainerRef,
  ViewChild,
  ChangeDetectorRef,
  AfterViewInit,
  ComponentFactoryResolver,
  OnDestroy,
  Injector,
} from '@angular/core';
import cloneDeep from 'lodash-es/cloneDeep';
import ResizeObserver from 'resize-observer-polyfill';

import { ModalCountService } from '../modal-count.service';
import { fuiModalRef } from '../modal-ref.service';
import { fuiModalConfig, ComponentType } from '../modal.model';
import { fui_MODAL_DATA } from '../modal-token';

let modalId = 0;

const count = new ModalCountService();

@Component({
  templateUrl: './modal-container.component.html',
  providers: [
    fuiModalRef,
  ],
})
export class ModalContainerComponent implements OnInit, AfterViewInit, OnDestroy {
  @HostBinding('class.fui-modal') hostClass = true;

  @ViewChild('backdrop') backdrop: ElementRef;

  @ViewChild('dialog') dialog: ElementRef;

  // 标题
  title: string;

  // 尺寸
  size: string;

  // 是否允许点击背景关闭弹窗
  backdropClose: boolean;

  // 当前组件的实例，在关闭对话框时，需要手动销毁
  // componentRef: ComponentRef<ModalContainerComponent>;

  // 对话框内部挂载组件的容器
  @ViewChild('modalBody', {
    read: ViewContainerRef,
    static: true,
  }) modalBody: ViewContainerRef;

  // 监听dialog高度变化的observer
  sizeObserver: ResizeObserver;

  // 对话框内部挂载的组件
  modalBodyComponent: ComponentType<any>;

  // 对话框内部挂载组件的配置信息
  config: fuiModalConfig;

  // 当前对话框的id
  id = ++modalId;

  // 在对话框开启时，监听document keydown事件的回调
  documentEscapeListener = (function(event) {
    const {keyCode} = event;
    if (keyCode !== 27) {
      return;
    }
    if (this.count.last === this.id) {
      this.close(null);
    }
  }).bind(this);

  // 已打开对话框的计数器
  count = count;

  constructor(
    private element: ElementRef,
    private componentFactoryResolver: ComponentFactoryResolver,
    private changeDetectorRef: ChangeDetectorRef,
    public modalRef: fuiModalRef,
  ) { }

  ngOnInit() {
    this.open();
    const clean = this.clean.bind(this);
    this.modalRef.afterClose
    .subscribe({ complete: clean });
  }

  ngAfterViewInit() {
    if (!this.modalBodyComponent) {
      return;
    }
    const {data = {}, title, size = 'md', backdropClose = true, injector: parentInjector} = this.config;

    // 处理注入依赖
    const injector = Injector.create({
      providers: [
        {
          provide: fui_MODAL_DATA,
          useValue: cloneDeep(data),
        },
        {
          provide: fuiModalRef,
          useValue: this.modalRef,
        },
      ],
      parent: parentInjector,
    });

    // 生成内部组件实例
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.modalBodyComponent);
    const childView = this.modalBody.createComponent(componentFactory, undefined, injector);

    this.changeDetectorRef.detach();
    this.title = title;
    this.size = size;
    this.backdropClose = backdropClose;
    this.changeDetectorRef.markForCheck();
    this.changeDetectorRef.detectChanges();
    this.changeDetectorRef.reattach();

    this.startListenDialogHeight();
  }

  ngOnDestroy() {
    this.stopListenDialogHeight();
  }

  startListenDialogHeight() {
    this.sizeObserver = new ResizeObserver(() => {
      this.updateBackdropHeight();
    });
    this.sizeObserver.observe(this.dialog.nativeElement);
  }

  stopListenDialogHeight() {
    if (this.sizeObserver) {
      this.sizeObserver.disconnect();
    }
  }

  open() {
    this.count.push(this.id);
    document.body.classList.add('fui-modal-open');
    document.body.appendChild(this.element.nativeElement);
    document.addEventListener('keydown', this.documentEscapeListener);
  }

  clean() {
    this.count.pop();
    if (this.count.empty) {
      document.body.classList.remove('fui-modal-open');
    }
    document.removeEventListener('keydown', this.documentEscapeListener);
  }

  updateBackdropHeight() {
    this.backdrop.nativeElement.style.height = '100vh';
    this.backdrop.nativeElement.style.height = this.element.nativeElement.scrollHeight + 'px';
  }

  // 通过点击背景或关闭按钮方式关闭对话框
  close() {
    this.modalRef.afterClose.complete();
  }
}
